package ar.edu.unrn.lia.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;

@Entity
@Table(name = "alquiler")
public class Alquiler extends BaseEntity implements Serializable {

	
	static final  Integer ALERTA_VENCIMIENTO_CONTRATO = 30; 

	
	public enum AlquilerEstado {
		ACTIVO("Activo"), CANCELADO("Cancelado"), FINALIZADO("Finalizado"),PROXIMO_VENCER("Proximo a Vencer");

		private String estado;

		private AlquilerEstado(String estado) {
			this.estado = estado;
		}

		@Override
		public String toString() {
			return estado;
		}
	}

	private static final long serialVersionUID = -3943008313790804160L;

	private Inquilino inquilino;
	
	private Garante garante;

	private AlquilerEstado estado = AlquilerEstado.ACTIVO;

	private Propiedad propiedad;

	private Date fechaInicio;

	private Date fechaFin;
	
	private Date fechaProximaVencer;

	private byte[] contrato;

	private List<AlquilerPeriodo> periodos = new ArrayList<AlquilerPeriodo>(0);

	private List<AlquilerCuota> cuotas = new ArrayList<AlquilerCuota>(0);

	private List<CuotaPreIngreso> cuotasPreIngreso = new ArrayList<CuotaPreIngreso>(0);
	
	private Boolean contratoPropio = true;

	public Alquiler() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Alquiler(Propiedad propiedad) {
		super();
		this.propiedad = propiedad;
		
		addPeriodo(new AlquilerPeriodo(1, 12, propiedad.getMontoMinimoAlquiler()));
		
	}

	@NotNull(message = "{name.notnull}")
	@ManyToOne(optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "inquilino_id", referencedColumnName = "id")
	public Inquilino getInquilino() {
		return inquilino;
	}

	public void setInquilino(Inquilino inquilino) {
		this.inquilino = inquilino;
	}
	
	
	@NotNull(message = "{name.notnull}")
	@ManyToOne(optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "garante_id", referencedColumnName = "id")
	public Garante getGarante() {
		return garante;
	}

	public void setGarante(Garante garante) {
		this.garante = garante;
	}

	@NotNull(message = "{name.notnull}")
	@ManyToOne(optional = false, fetch = FetchType.LAZY)
	@JoinColumn(name = "propiedad_id", referencedColumnName = "id")
	public Propiedad getPropiedad() {
		return propiedad;
	}

	public void setPropiedad(Propiedad propiedad) {
		this.propiedad = propiedad;
	}

	@Lob
	@Basic(fetch = FetchType.LAZY)
	@Column(length = 100000, nullable = true)
	public byte[] getContrato() {
		return contrato;
	}

	public void setContrato(byte[] contrato) {
		this.contrato = contrato;
	}

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "alquiler", fetch = FetchType.LAZY)
	public List<AlquilerPeriodo> getPeriodos() {
		return periodos;
	}

	public void setPeriodos(List<AlquilerPeriodo> periodos) {
		this.periodos = periodos;
	}

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "alquiler", fetch = FetchType.LAZY)
	public List<AlquilerCuota> getCuotas() {
		return cuotas;
	}

	public void setCuotas(List<AlquilerCuota> cuotas) {
		this.cuotas = cuotas;
	}

	@OneToMany(cascade = CascadeType.ALL, mappedBy = "alquiler", fetch = FetchType.LAZY)
	public List<CuotaPreIngreso> getCuotasPreIngreso() {
		return cuotasPreIngreso;
	}

	public void setCuotasPreIngreso(List<CuotaPreIngreso> cuotasPreIngreso) {
		this.cuotasPreIngreso = cuotasPreIngreso;
	}

	@Enumerated(EnumType.STRING)
	@NotNull(message = "{name.notnull}")
	public AlquilerEstado getEstado() {
		return estado;
	}

	public void setEstado(AlquilerEstado estado) {
		this.estado = estado;
	}

	@Temporal(TemporalType.DATE)
	@NotNull(message = "{name.notnull}")
	public Date getFechaInicio() {
		return fechaInicio;
	}

	public void setFechaInicio(Date fechaInicio) {
		this.fechaInicio = fechaInicio;
	}

	@Temporal(TemporalType.DATE)
	@NotNull(message = "{name.notnull}")
	public Date getFechaFin() {
		return fechaFin;
	}

	public void setFechaFin(Date fechaFin) {
		this.fechaFin = fechaFin;
	}
	
	
	@Temporal(TemporalType.DATE)
	public Date getFechaProximaVencer() {
		return fechaProximaVencer;
	}
	
	
	public void setFechaProximaVencer(Date fechaProximaVencer) {
		this.fechaProximaVencer = fechaProximaVencer;
	}
	
	
	
	@NotNull(message = "{name.notnull}")
	public Boolean getContratoPropio() {
		return contratoPropio;
	}

	public void setContratoPropio(Boolean contratoPropio) {
		this.contratoPropio = contratoPropio;
	}

	public void removeCuota(AlquilerCuota cuota) {
		cuota.setAlquiler(null);
		getCuotas().remove(cuota);
	}

	public void addCuota(AlquilerCuota cuota) {
		cuota.setAlquiler(this);
		getCuotas().add(cuota);
	}

	public void addPeriodo(AlquilerPeriodo periodo) {
		periodo.setAlquiler(this);
		getPeriodos().add(periodo);
		periodo.setNroPeriodo(getPeriodos().size());
	}

	public void removePeriodo(AlquilerPeriodo periodo) {
		periodo.setAlquiler(null);
		getPeriodos().remove(periodo);
	}

	public void calcularFechaFIn() {
		Calendar caFechaFin = Calendar.getInstance();
		caFechaFin.setTime(getFechaInicio());
		for (AlquilerPeriodo periodoAlquiler : getPeriodos()) {
			caFechaFin.add(Calendar.MONTH, periodoAlquiler.getPeriodo());
		}
		setFechaFin(caFechaFin.getTime());	
		
		caFechaFin.add(Calendar.DATE, -ALERTA_VENCIMIENTO_CONTRATO);
		
		setFechaProximaVencer(caFechaFin.getTime());		
	}

	// TODO Primer Cuota monto diferencia, resto 1/mes/anio
	public void calcularCoutas() {
		int nroCuota = 1;
		for (AlquilerPeriodo p : getPeriodos()) {
			for (int i = 1; i <= p.getPeriodo(); i++) {
				addCuota(new AlquilerCuota(nroCuota,
						p.getMonto(),
						p.calculaMontoCuotaPropietario(), 
						fechaCuota(getFechaInicio(), nroCuota)));
				nroCuota++;
			}
		}
	}

	private Date fechaCuota(Date fechaInicioAlquiler, Integer nroCuota) {
		
		Calendar fechaCuota = Calendar.getInstance();
		fechaCuota.setTime(fechaInicioAlquiler);			
		if (fechaCuota.get(Calendar.DATE)<=15)
		{
			fechaCuota.add(Calendar.MONTH, -1);
		}
		
		fechaCuota.set(Calendar.DATE, 1);
		fechaCuota.add(Calendar.MONTH, nroCuota);
		return fechaCuota.getTime();
	}
	
	public Integer diasAjuste()
	{
		Calendar fechaCuota = Calendar.getInstance();
		fechaCuota.setTime(this.getFechaInicio());		
		return 31 - fechaCuota.get(Calendar.DATE);
	}
	
	public Integer montoBase() {
		return getPeriodos().get(0).getMonto();
	}

	public Integer montoTotal() {
		Integer resultado = 0;
		for (AlquilerCuota cuota : getCuotas()) {
			resultado = resultado + cuota.getMontoInquilino();
		}
		return resultado;
	}

	public Double montoDeudaPreIncripcion()
	{
		Double resultado = 0D;
		for (CuotaPreIngreso cp : getCuotasPreIngreso()) {
			resultado = resultado + cp.montoDeuda();			
		}
		//TODO Redondeo, a 2 decimales
		resultado = Math.round(resultado*100.0)/100.0;		
		return resultado;
	}
	
	public Double montoPreIngresoTotal()
	{
		Double resultado = 0D;
		for (CuotaPreIngreso cp : getCuotasPreIngreso()) {
			if(cp.getMonto()>0)
			resultado = resultado + cp.getMonto();			
		}
		resultado = Math.round(resultado*100.0)/100.0;	
		return resultado;
	}
	
	public Double montoPreIngresoPagado()
	{
		Double resultado = 0D;
		for (CuotaPreIngreso cp : getCuotasPreIngreso()) {
			resultado = resultado + cp.getMontoPagado();			
		}
		return resultado;
	}
	
	public Double montoDeudaCuota()
	{
		Double resultado = 0D;
		for (AlquilerCuota ac : getCuotas()) {
			resultado = resultado + ac.montoDeuda();			
		}
		return resultado;
	}
	
	public Double montoDeudaTotal()
	{
		return montoDeudaPreIncripcion() + montoDeudaCuota();
	}	
	
	public Integer montoDeudaPagoParcial()
	{	Integer resultado = 0;
		for (AlquilerCuota c : getCuotas()) {
			resultado = resultado + c.montoDeudaPagoParcial();			
		}
		return resultado;
	}
	
	public Integer montoDeudaCuotasVencidas()
	{	Integer resultado = 0;
		for (AlquilerCuota c : getCuotas()) {
			resultado = resultado + c.montoDeudaVencida();			
		}
		return resultado;
	}
	
	public Boolean preIngresoPago()
	{
		return montoDeudaPreIncripcion().equals(0D);
	}
	
}
